home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Libris Britannia 4
/
science library(b).zip
/
science library(b)
/
PROGRAMM
/
DB_CLIPP
/
0277.ZIP
/
SQ2.C
< prev
next >
Wrap
Text File
|
1986-02-28
|
36KB
|
1,418 lines
static char *sccsid = "@(#)sq2.c 1.9u (UCF) 86/02/14";
/*
* sq2.c - CP/M compatible file squeezer utility
*
*/
#include <stdio.h>
#ifdef MSDC86
#include <time.h>
#define FBRDWR "wrb"
#define FBRD "rb"
#endif
#ifdef CPMC86
#define FBRDWR "wrb"
#define FBRD "rb"
#define strupr upper
#endif
#ifdef MSDMSC
#include <time.h>
#include <dos.h>
#include <fcntl.h>
#include <sys\types.h>
#include <sys\stat.h>
#include <io.h>
#define BWRITE O_RDWR|O_CREAT|O_BINARY
#define BPERMS S_IREAD|S_IWRITE
#define BREAD O_RDONLY|O_BINARY
#define FBRDWR "w+b"
#define FBRD "rb"
#endif
#define TRUE 1
#define FALSE 0
#define ERROR (-1)
#define PATHLEN 64 /* Number of characters allowed in pathname */
/* Definitions and external declarations */
#define RECOGNIZE 0xFF76 /* unlikely pattern */
/* *** Stuff for first translation module *** */
#define DLE 0x90
/* *** Stuff for second translation module *** */
#define SPEOF 256 /* special endfile token */
#define NUMVALS 257 /* 256 data values plus SPEOF*/
typedef int INT;
typedef unsigned UNSIGNED;
/* Definitions and external declarations */
INT Usestd; /* Use stdout for squeezed output */
UNSIGNED crc; /* error check code */
/* *** Stuff for first translation module *** */
INT likect; /*count of consecutive identical chars */
INT lastchar, newchar;
unsigned char state;
/* states */
#define NOHIST 0 /*don't consider previous input*/
#define SENTCHAR 1 /*lastchar set, no lookahead yet */
#define SENDNEWC 2 /*newchar set, previous sequence done */
#define SENDCNT 3 /*newchar set, DLE sent, send count next */
/* *** Stuff for second translation module *** */
#define NOCHILD -1 /* indicates end of path through tree */
#define NUMNODES (NUMVALS + NUMVALS - 1) /* nbr of nodes */
#define MAXCOUNT (UNSIGNED) 65535 /* biggest UNSIGNED integer */
/* The following array of structures are the nodes of the
* binary trees. The first NUMVALS nodes becomethe leaves of the
* final tree and represent the values of the data bytes being
* encoded and the special endfile, SPEOF.
* The remaining nodes become the internal nodes of the final tree.
*/
struct nd {
UNSIGNED weight; /* number of appearances */
INT tdepth; /* length on longest path in tre*/
INT lchild, rchild; /* indexes to next level */
} node[NUMNODES];
INT dctreehd; /*index to head node of final tree */
/* This is the encoding table:
* The bit strings have first bit in low bit.
* Note that counts were scaled so code fits UNSIGNED integer
*/
INT codelen[NUMVALS]; /* number of bits in code */
UNSIGNED code[NUMVALS]; /* code itself, right adjusted */
UNSIGNED tcode; /* temporary code value */
/* Variables used by encoding process */
INT curin; /* Value currently being encoded */
INT cbitsrem; /* Number of code string bits remaining */
UNSIGNED ccode; /* Current code shifted so next code bit is at right */
/* This program compresses a file without losing information.
* The usq2.com program is required to unsqueeze the file
* before it can be used.
*
* Typical compression rates are:
* .COM 6% (Don't bother)
* .ASM 33% (using full ASCII set)
* .DIC 46% (using only uppercase and a few others)
* Squeezing a really big file takes a few minutes.
*
* Useage:
* sq2 file ...
*
*
* The squeezed file name is formed by changing the second from last
* letter of the file type to Q. If there is no file type,
* the squeezed file type is QQQ. If the name exists it is
* overwritten!
*
* The transformations compress strings of identical bytes and
* then encode each resulting byte value and EOF as bit strings
* having lengths in inverse proportion to their frequency of
* occurrence in the intermediate input stream. The latter uses
* the Huffman algorithm. Decoding information is included in
* the squeezed file, so squeezing short files or files with
* uniformly distributed byte values will actually increase size.
*/
/* CHANGE HISTORY:
* 1.5u **nix version - means output to stdout.
* (stdin not allowed becuase sq needs to rewind input, which
* won't work with pipes.)
* Filename generation changed to suit **nix and stdio.
* 1.6u machine independent output for file compatibility with
* original CP/M version SQ, when running on machine with
* IBM byte ordering such as Z8000 and 68000.
* 1.7u machine independence was still lacking for 32-bit machines
* like the VAX-11/780, so a typedef was added. No action
* need be taken if running on a 16-bit machine, but if
* running on a VAX, define VAX either on the cc line or
* in the program preamble. Ben Goldfarb 12/13/82
* 1.8u Modified to run under CI-86 compiler for the IBM PC
* Robert J. Beilstein 09/02/83
* 1.9u CI-86 source modified to squeeze files to floppy disk which
* are larger than the floppy disk. Similar to BACKUP.COM.
* Ark Software 07.01.86
* Code for MSC and CPM86 - 23.02.86
*
*/
#define VERSION "1.9u 14-02-86"
INT inbackground = 0; /* change to 1 to suppress informative messages */
INT buildenc(), gethuff(), getc_crc();
/* new globals for version 1.9u
---------------------------- */
long free_space = 0L;
INT bkup_id = 0;
INT span_cnt = 0;
char tgt_drv[20];
unsigned char outfile[PATHLEN+2]; /* output file spec. */
unsigned char infile[PATHLEN+2];
FILE *inbuff, *outbuff; /* file buffers */
#define SPAN_CHR 0xff /* marks incomplete restore/file */
#define FIN_CHR 0x00 /* marks last disk/last segment */
/* structure of SQZID.@@@ */
struct bki {
unsigned char bk_fflag; /* FF=middle disk 00=last disk */
int bk_seqn; /* disk sequence number */
long bk_date; /* backup date in long format - seconds since 01.01.70 */
char bk_spare[121];
} bkid;
/* structure of SQUEEZED file header */
struct sqh {
unsigned char sq_fflag; /* FF=incomplete 00=last segment */
int sq_span; /* span count */
int sq_nulo;
char sq_path[65]; /* path + filename */
char sq_spare[58]; /* spare */
} sqid;
#define FNSZE 81
/* structure of file info block from wildexp modules */
struct fil_info {
unsigned attrib; /* attribute found (int for future UNIX) */
unsigned ftime; /* file time MSDOS format */
unsigned fdate; /* file date MSDOS format */
long fsize;
char fil_nam[FNSZE]; /* ASCIIZ file name - maximum which is not all allocated by sbrk */
};
struct fil_info *finfo;
long pspace();
main(argc, argv)
INT argc;
char *argv[];
{
register INT i,c;
if(argc < 3)
usage();
strcpy(tgt_drv, argv[argc - 1]);
if(toupper(tgt_drv[0]) > 'B' || toupper(tgt_drv[0]) < 'A')
abort("ERR - target must be drive A: or drive B:");
if(hasbad(tgt_drv))
usage();
strupr(tgt_drv);
argc--;
#ifdef MSDC86
if (wildexp(&argc,&argv,0) == ERROR)
{
abort("ERR - wildcard extraction error\n\n\n");
}
time(&bkid.bk_date);
#endif
#ifdef CPMC86
if (wildexp(&argc,&argv) == ERROR)
{
abort("ERR - wildcard extraction error\n\n\n");
}
bkid.bk_date = 123456L;
#endif
/* Process the parameters in order */
#ifdef MSDC86
for(i = 1; i < argc; ++i)
{
finfo = (struct fil_info *) *++argv;
obey(finfo->fil_nam);
}
#else
for(i = 1; i < argc; ++i)
{
obey(*++argv);
}
#endif
exit(0);
}
obey(p)
unsigned char *p;
{
unsigned char *w,*q;
extern char *strrchr();
/* First build output file name */
strcpy(outfile, tgt_drv);
span_cnt = 0;
w = p;
if((q = strrchr(w,'/')) != NULL)
w = q + 1;
if((q = strrchr(w,'\\')) != NULL)
w = q + 1;
if((q = strrchr(w,':')) != NULL)
w = q + 1;
strcat(outfile, w);
if((q = strrchr(outfile,'.')) != NULL)
if(*(q+1) == '\0')
q = '\0';
strcpy(infile,p);
strupr(infile);
squeeze();
}
squeeze()
{
register INT i, c;
long fseek();
/* enough room for "RESTORE" header and more? */
if(free_space < 512L)
new_disk();
if (!inbackground)
fprintf(stderr, "%s -> %s: ", infile, outfile);
if((inbuff=fopen(infile, FBRD)) == NULL)
{
fprintf(stderr, "sq2: can't open %s\n", infile);
return;
}
if((outbuff=fopen(outfile, FBRDWR)) == NULL)
{
fprintf(stderr, "sq2: can't create %s\n", outfile);
fclose(inbuff);
return;
}
/* First pass - get properties of file */
crc = 0; /* initialize checksum */
if (!inbackground)
fprintf(stderr, "analyzing, ");
init_ncr();
/* analyse file - make code-decode tree */
init_huff(inbuff);
/* rewind file */
fseek(inbuff,0L,0);
/* write out "RESTORE" header */
wrt_sqhead(infile);
/* Write output file header with decoding info */
wrt_head(infile);
/* Second pass - encode the file */
if (!inbackground)
fprintf(stderr, "squeezing, ");
init_ncr(); /* For second pass */
/* Translate the input file into the output file */
while((c = gethuff(inbuff)) != EOF)
{
if(putc(c, outbuff) == ERROR && ferror(outbuff))
{
abort("\nsq2: write error\n");
}
if(--free_space == 0L)
{
span_cnt++;
new_disk();
}
}
if (!inbackground)
fprintf(stderr, " done.\n");
closeall:
fclose(inbuff);
closeout:
fflush(outbuff);
fclose(outbuff);
free_space = pspace(tgt_drv);
}
/* First translation - encoding of repeated characters
* The code is byte for byte pass through except that
* DLE is encoded as DLE, zero and repeated byte values
* are encoded as value, DLE, count for count >= 3.
*/
init_ncr() /*initialize getcnr() */
{
state = NOHIST;
}
INT
getcnr(iob)
FILE *iob;
{
switch(state) {
case NOHIST:
/* No relevant history */
state = SENTCHAR;
return lastchar = getc_crc(iob);
case SENTCHAR:
/* Lastchar is set, need lookahead */
switch(lastchar) {
case DLE:
state = NOHIST;
return 0; /* indicates DLE was the data */
case EOF:
return EOF;
default:
for(likect = 1; (newchar = getc_crc(iob)) == lastchar && likect < 255; ++likect)
;
switch(likect) {
case 1:
return lastchar = newchar;
case 2:
/* just pass through */
state = SENDNEWC;
return lastchar;
default:
state = SENDCNT;
return DLE;
}
}
case SENDNEWC:
/* Previous sequence complete, newchar set */
state = SENTCHAR;
return lastchar = newchar;
case SENDCNT:
/* Sent DLE for repeat sequence, send count */
state = SENDNEWC;
return likect;
default:
fprintf(stderr,"sq2: Bug - bad state\n");
exit(1);
/* NOTREACHED */
}
}
/******** Second translation - bytes to variable length bit strings *********/
/* This translation uses the Huffman algorithm to develop a
* binary tree representing the decoding information for
* a variable length bit string code for each input value.
* Each string's length is in inverse proportion to its
* frequency of appearance in the incoming data stream.
* The encoding table is derived from the decoding table.
*
* The range of valid values into the Huffman algorithm are
* the values of a byte stored in an integer plus the special
* endfile value chosen to be an adjacent value. Overall, 0-SPEOF.
*
* The "node" array of structures contains the nodes of the
* binary tree. The first NUMVALS nodes are the leaves of the
* tree and represent the values of the data bytes being
* encoded and the special endfile, SPEOF.
* The remaining nodes become the internal nodes of the tree.
*
* In the original design it was believed that
* a Huffman code would fit in the same number of
* bits that will hold the sum of all the counts.
* That was disproven by a user's file and was a rare but
* infamous bug. This version attempts to choose among equally
* weighted subtrees according to their maximum depths to avoid
* unnecessarily long codes. In case that is not sufficient
* to guarantee codes <= 16 bits long, we initially scale
* the counts so the total fits in an unsigned integer, but
* if codes longer than 16 bits are generated the counts are
* rescaled to a lower ceiling and code generation is retried.
*/
/* Initialize the Huffman translation. This requires reading
* the input file through any preceding translation functions
* to get the frequency distribution of the various values.
*/
init_huff(ib)
FILE *ib;
{
register INT c, i;
INT btlist[NUMVALS]; /* list of intermediate binary trees */
INT listlen; /* length of btlist */
UNSIGNED *wp; /* simplifies weight counting */
UNSIGNED ceiling; /* limit for scaling */
/* Initialize tree nodes to no weight, no children */
init_tree();
/* Build frequency info in tree */
do {
c = getcnr(ib);
if(c == EOF)
c = SPEOF;
if(*(wp = &node[c].weight) != MAXCOUNT)
++(*wp);
}
while(c != SPEOF);
ceiling = MAXCOUNT;
do { /* Keep trying to scale and encode */
if(ceiling != MAXCOUNT)
fprintf(stderr, "sq2: *** rescaling ***, ");
scale(ceiling);
ceiling /= 2; /* in case we rescale */
/* Build list of single node binary trees having
* leaves for the input values with non-zero counts
*/
for(i = listlen = 0; i < NUMVALS; ++i)
if(node[i].weight != 0) {
node[i].tdepth = 0;
btlist[listlen++] = i;
}
/* Arrange list of trees into a heap with the entry
* indexing the node with the least weight at the top.
*/
heap(btlist, listlen);
/* Convert the list of trees to a single decoding tree */
bld_tree(btlist, listlen);
/* Initialize the encoding table */
init_enc();
/* Try to build encoding table.
* Fail if any code is > 16 bits long.
*/
}
while(buildenc(0, dctreehd) == ERROR);
/* Initialize encoding variables */
cbitsrem = 0; /*force initial read */
curin = 0; /*anything but endfile*/
}
/* The count of number of occurrances of each input value
* have already been prevented from exceeding MAXCOUNT.
* Now we must scale them so that their sum doesn't exceed
* ceiling and yet no non-zero count can become zero.
* This scaling prevents errors in the weights of the
* interior nodes of the Huffman tree and also ensures that
* the codes will fit in an unsigned integer. Rescaling is
* used if necessary to limit the code length.
*/
scale(ceil)
UNSIGNED ceil; /* upper limit on total weight */
{
register INT i,c;
INT ovflw, divisor;
UNSIGNED w, sum;
unsigned char increased; /* flag */
do {
for(i = sum = ovflw = 0; i < NUMVALS; ++i) {
if(node[i].weight > (ceil - sum))
++ovflw;
sum += node[i].weight;
}
divisor = ovflw + 1;
/* Ensure no non-zero values are lost */
increased = FALSE;
for(i = 0; i < NUMVALS; ++i) {
w = node[i].weight;
if (w < divisor && w != 0) {
/* Don't fail to provide a code if it's used at all */
node[i].weight = divisor;
increased = TRUE;
}
}
}
while(increased);
/* Scaling factor choosen, now scale */
if(divisor > 1)
for(i = 0; i < NUMVALS; ++i)
node[i].weight /= divisor;
}
/* heap() and adjust() maintain a list of binary trees as a
* heap with the top indexing the binary tree on the list
* which has the least weight or, in case of equal weights,
* least depth in its longest path. The depth part is not
* strictly necessary, but tends to avoid long codes which
* might provoke rescaling.
*/
heap(list, length)
INT list[], length;
{
register INT i;
for(i = (length - 2) / 2; i >= 0; --i)
adjust(list, i, length - 1);
}
/* Make a heap from a heap with a new top */
adjust(list, top, bottom)
INT list[], top, bottom;
{
register INT k, temp;
k = 2 * top + 1; /* left child of top */
temp = list[top]; /* remember root node of top tree */
if( k <= bottom) {
if( k < bottom && cmptrees(list[k], list[k + 1]))
++k;
/* k indexes "smaller" child (in heap of trees) of top */
/* now make top index "smaller" of old top and smallest child */
if(cmptrees(temp, list[k])) {
list[top] = list[k];
list[k] = temp;
/* Make the changed list a heap */
adjust(list, k, bottom); /*recursive*/
}
}
}
/* Compare two trees, if a > b return true, else return false
* note comparison rules in previous comments.
*/
cmptrees(a, b)
INT a, b; /* root nodes of trees */
{
if(node[a].weight > node[b].weight)
return TRUE;
if(node[a].weight == node[b].weight)
if(node[a].tdepth > node[b].tdepth)
return TRUE;
return FALSE;
}
/* HUFFMAN ALGORITHM: develops the single element trees
* into a single binary tree by forming subtrees rooted in
* interior nodes having weights equal to the sum of weights of all
* their descendents and having depth counts indicating the
* depth of their longest paths.
*
* When all trees have been formed into a single tree satisfying
* the heap property (on weight, with depth as a tie breaker)
* then the binary code assigned to a leaf (value to be encoded)
* is then the series of left (0) and right (1)
* paths leading from the root to the leaf.
* Note that trees are removed from the heaped list by
* moving the last element over the top element and
* reheaping the shorter list.
*/
bld_tree(list, len)
INT list[];
INT len;
{
register INT freenode; /* next free node in tree */
register struct nd *frnp; /* free node pointer */
INT lch, rch; /* temporaries for left, right children */
INT i;
/* Initialize index to next available (non-leaf) node.
* Lower numbered nodes correspond to leaves (data values).
*/
freenode = NUMVALS;
while(len > 1) {
/* Take from list two btrees with least weight
* and build an interior node pointing to them.
* This forms a new tree.
*/
lch = list[0]; /* This one will be left child */
/* delete top (least) tree from the list of trees */
list[0] = list[--len];
adjust(list, 0, len - 1);
/* Take new top (least) tree. Reuse list slot later */
rch = list[0]; /* This one will be right child */
/* Form new tree from the two least trees using
* a free node as root. Put the new tree in the list.
*/
frnp = &node[freenode]; /* address of next free node */
list[0] = freenode++; /* put at top for now */
frnp->lchild = lch;
frnp->rchild = rch;
frnp->weight = node[lch].weight + node[rch].weight;
frnp->tdepth = 1 + maxchar(node[lch].tdepth, node[rch].tdepth);
/* reheap list to get least tree at top*/
adjust(list, 0, len - 1);
}
dctreehd = list[0]; /*head of final tree */
}
/* ???????????? */
maxchar(a, b)
{
return a > b ? a : b;
}
/* Initialize all nodes to single element binary trees
* with zero weight and depth.
*/
init_tree()
{
register INT i;
for(i = 0; i < NUMNODES; ++i) {
node[i].weight = 0;
node[i].tdepth = 0;
node[i].lchild = NOCHILD;
node[i].rchild = NOCHILD;
}
}
init_enc()
{
register INT i;
/* Initialize encoding table */
for(i = 0; i < NUMVALS; ++i) {
codelen[i] = 0;
}
}
/* Recursive routine to walk the indicated subtree and level
* and maintain the current path code in bstree. When a leaf
* is found the entire code string and length are put into
* the encoding table entry for the leaf's data value .
*
* Returns ERROR if codes are too long.
*/
INT /* returns ERROR or NULL */
buildenc(level, root)
INT level;/* level of tree being examined, from zero */
INT root; /* root of subtree is also data value if leaf */
{
register INT l, r;
unsigned int shifty = 0xffff;
l = node[root].lchild;
r = node[root].rchild;
if( l == NOCHILD && r == NOCHILD) {
/* Leaf. Previous path determines bit string
* code of length level (bits 0 to level - 1).
* Ensures unused code bits are zero.
*/
codelen[root] = level;
code[root] = tcode & (shifty >> (16 - level));
return (level > 16) ? ERROR : NULL;
}
else {
if( l != NOCHILD) {
/* Clear path bit and continue deeper */
tcode &= ~(1 << level);
/* NOTE RECURSION */
if(buildenc(level + 1, l) == ERROR)
return ERROR;
}
if(r != NOCHILD) {
/* Set path bit and continue deeper */
tcode |= 1 << level;
/* NOTE RECURSION */
if(buildenc(level + 1, r) == ERROR)
return ERROR;
}
}
return NULL; /* if we got here we're ok so far */
}
/* Write out the sq header of the compressed file */
wrt_head(infile)
char *infile;
{
register INT l,r;
INT i, k;
INT numnodes; /* nbr of nodes in simplified tree */
putwe(RECOGNIZE); /* identifies as compressed */
putwe(crc); /* unsigned sum of original data */
/* Write out a simplified decoding tree. Only the interior
* nodes are written. When a child is a leaf index
* (representing a data value) it is recoded as
* -(index + 1) to distinguish it from interior indexes
* which are recoded as positive indexes in the new tree.
* Note that this tree will be empty for an empty file.
*/
numnodes = dctreehd < NUMVALS ? 0 : dctreehd - (NUMVALS -1);
putwe(numnodes);
for(k = 0, i = dctreehd; k < numnodes; ++k, --i) {
l = node[i].lchild;
r = node[i].rchild;
l = l < NUMVALS ? -(l + 1) : dctreehd - l;
r = r < NUMVALS ? -(r + 1) : dctreehd - r;
putwe(l); /* left child */
putwe(r); /* right child */
}
}
/* Get an encoded byte or EOF. Reads from specified stream AS NEEDED.
*
* There are two unsynchronized bit-byte relationships here.
* The input stream bytes are converted to bit strings of
* various lengths via the static variables named c...
* These bit strings are concatenated without padding to
* become the stream of encoded result bytes, which this
* function returns one at a time. The EOF (end of file) is
* converted to SPEOF for convenience and encoded like any
* other input value. True EOF is returned after that.
*
* The original gethuff() called a seperate function,
* getbit(), but that more readable version was too slow.
*/
INT /* Returns byte values except for EOF */
gethuff(ib)
FILE *ib;
{
INT rbyte; /* Result byte value */
INT need, take; /* numbers of bits */
rbyte = 0;
need = 8; /* build one byte per call */
/* Loop to build a byte of encoded data
* Initialization forces read the first time
*/
loop:
if(cbitsrem >= need) {
/* Current code fullfills our needs */
if(need == 0)
return rbyte;
/* Take what we need */
rbyte |= ccode << (8 - need);
/* And leave the rest */
ccode >>= need;
cbitsrem -= need;
return rbyte & 0xff;
}
/* We need more than current code */
if(cbitsrem > 0) {
/* Take what there is */
rbyte |= ccode << (8 - need);
need -= cbitsrem;
}
/* No more bits in current code string */
if(curin == SPEOF) {
/* The end of file token has been encoded. If
* result byte has data return it and do EOF next time
*/
cbitsrem = 0;
/*NOTE: +0 is to fight compiler bug? */
return (need == 8) ? EOF : rbyte + 0;
}
/* Get an input byte */
if((curin = getcnr(ib)) == EOF)
curin = SPEOF; /* convenient for encoding */
/* Get the new byte's code */
ccode = code[curin];
cbitsrem = codelen[curin];
goto loop;
}
/* Get next byte from file and update checksum */
INT
getc_crc(ib)
FILE *ib;
{
register INT c;
c = getc(ib);
if(c != EOF)
crc += c; /* checksum */
return c;
}
/* Output functions with error reporting */
putce(c)
INT c;
{
if(putc(c, outbuff) == ERROR && ferror(outbuff)) {
abort("\nsq2: write error\n");
}
if(--free_space == 0L)
{
span_cnt++;
if(!new_disk())
abort("\nsq2: write error");
}
}
/*
* machine independent put-word that writes low order byte first
* (compatible with CP/M original) regardless of host cpu.
*/
putwe(w)
INT w;
{
putc(w, outbuff);
if(--free_space == 0L)
{
span_cnt++;
if(!new_disk())
abort("\nsq2: write error");
}
putc(w>>8, outbuff);
if(--free_space == 0L)
{
span_cnt++;
if(!new_disk())
abort("\nsq2: write error");
}
}
/* ******************** new functions for ver. 1.9u ******************** */
usage()
{
fprintf(stderr,"\n\nFile squeezer version %s by\n\n\tRichard Greenlaw\n\t251 Colony Ct.\n\tGahanna, Ohio 43230\n", VERSION);
fprintf(stderr,"\n\tMulti-disk version by Ark Software for the Public Domain");
fprintf(stderr,"\n\t **this program is free and not for sale**\n\n");
fprintf(stderr, "Usage: sq2 <filename> ... <filename> <target drive>\n\n");
fprintf(stderr, "i.e.: sq2 phial*.dat phial*.idx grep.c b:\n\n");
exit(1);
}
new_disk()
{
char filespec[16];
int fd;
/* is file spread over more than one disk? */
if(span_cnt > 0)
{
get_sqhdr(); /* read file "RESTORE" header */
sqid.sq_fflag = SPAN_CHR; /* mark as incomplete */
put_sqhdr(); /* write it back */
fclose(outbuff); /* and close file */
}
if(bkup_id != 0) /* indicate a full disk */
{
get_bkupid(tgt_drv);
bkid.bk_fflag = SPAN_CHR; /* full disk */
strcpy(filespec,tgt_drv);
strcat(filespec,"SQZID.@@@");
fd = open(filespec,BWRITE);
write(fd,&bkid,sizeof(bkid));
close(fd);
}
bkup_id++;
dsk_reset(tgt_drv);
fprintf(stderr,"\n\n%cInsert backup diskette %02d in drive %s\n",'\007',bkup_id, tgt_drv);
fprintf(stderr,"Warning! Diskette files will be erased\n");
fprintf(stderr,"Strike any key when ready... ");
bdos(1,0);
dsk_reset(tgt_drv);
fprintf(stderr,"\n\n*** Squeezing files to diskette %02d ***\n",bkup_id);
all_unlink(tgt_drv);
put_bkupid(tgt_drv,bkup_id);
/* get free space on new disk */
free_space = pspace(tgt_drv);
/* if a file is spanning disks, write out new "RESTORE" header */
if(span_cnt > 0)
{
if((outbuff=fopen(outfile, FBRDWR)) == NULL) {
fprintf(stderr, "sq2: can't create %s\n", outfile);
fclose(inbuff);
return(FALSE);
}
wrt_sqhead(infile);
fprintf(stderr, "%s -> %s: analysing, squeezing, ", infile, outfile);
}
return(TRUE);
}
/* get free space on drive */
#ifdef MSDC86
long pspace(drive)
unsigned char *drive;
{
struct { unsigned short ax,bx,cx,dx,si,di,ds,es; } srv;
extern int _sysvers;
if((_sysvers & 0xff) == 0)
abort("Wrong DOS version");
if(*(drive+1) == ':')
srv.dx = (toupper(*drive)-64) & 0x0f;
else
srv.dx = 0;
srv.ax = 0x3600;
if(sysint21(&srv,&srv) & 1)
abort("\nsq2: could not read disk space\n\n");
return((long)srv.bx * (long)srv.cx * (long)srv.ax);
}
#endif
#ifdef MSDMSC
long pspace(drive)
unsigned char *drive;
{
union REGS srv;
if(*(drive+1) == ':')
srv.x.dx = (toupper(*drive)-64) & 0x0f;
else
srv.x.dx = 0;
srv.h.ah = 0x36;
intdos(&srv, &srv);
if(srv.x.cflag)
{
abort("sq2: Could not read disk space");
exit(1);
}
return((long)srv.x.bx * (long)srv.x.cx * (long)srv.x.ax);
}
#endif
#ifdef CPMC86
long pspace(drive)
unsigned char *drive;
{
unsigned int all_bx, all_es, dpb_bx, dpb_es;
int cur_drv, bls, all_count, i, j;
long tot_cap, cap_left;
struct {
unsigned int spt;
unsigned char bsh;
unsigned char blm;
unsigned char exm;
unsigned int dsm;
unsigned int drm;
unsigned char al0;
unsigned char al1;
unsigned int cks;
unsigned int off;
} dpb;
unsigned char all_vec[200];
struct { unsigned short ax,bx,cx,dx,si,di,ds,es; } srv;
struct { unsigned int scs, sss, sds, ses; } rrv;
/* get current drive for reset */
srv.cx = 0x0019;
sysint(224,&srv,&srv);
cur_drv = (srv.ax & 0x00ff);
/* select disk as per drive */
srv.cx = 0x000e;
srv.dx = 0x0000 | (toupper(*drive) - 65);
sysint(224,&srv,&srv);
/* get file allocation vector */
srv.cx = 0x001b;
sysint(224,&srv,&srv);
all_bx = srv.bx;
all_es = srv.es;
/* get disk parameter block and copy to local structure */
segread(&rrv);
srv.cx = 0x001f;
sysint(224,&srv,&srv);
movblock(srv.bx,srv.es,&dpb,rrv.sds,sizeof(dpb));
/* select disk as before */
srv.cx = 0x000e;
srv.dx = cur_drv;
sysint(224,&srv,&srv);
/* now work out free space */
if(dpb.bsh==3 && dpb.blm == 7)
bls = 1024;
else if(dpb.bsh==4 && dpb.blm==15)
bls = 2048;
else if(dpb.bsh==5 && dpb.blm==31)
bls = 4096;
else if(dpb.bsh==6 && dpb.blm==63)
bls = 8192;
else if(dpb.bsh==7 && dpb.blm==127)
bls = 16384;
else
abort("\n\nsq2: DPB error\n\n");
/* this gives us total capacity on drive */
tot_cap = (long) bls * (long) dpb.dsm;
/* now - how much space used up re: alloc vector */
movblock(all_bx,all_es, all_vec,rrv.sds, (dpb.dsm/8) + 1);
for(all_count=0, i=0; i < ((dpb.dsm/8) +1); i++)
{
for(j=0; j<8; j++)
{
all_count += (all_vec[i] & 1);
all_vec[i] >>= 1;
}
}
cap_left = tot_cap - ((long) all_count * (long) bls);
return(cap_left);
}
#endif
/* delete all files on drive - must be limited to A: or B: */
all_unlink(drive)
unsigned char *drive;
{
char *next, *first, filespec[20];
extern char *filedir(), *strchr();
strcpy(filespec, drive);
if(strchr(filespec,':') == NULL)
strcat(filespec,":");
strcat(filespec,"*.*");
if((first = filedir(filespec,7)) == NULL)
return(0);
for(next = first; *next != NULL;)
{
strcpy(&filespec[2],next);
unlink(filespec);
next = next + strlen(next) + 1;
}
free(first);
}
/* make SQZID.@@@ */
put_bkupid(drive,id)
char *drive;
int id;
{
char filespec[16];
int fd;
bkid.bk_fflag = 0x00; /* last/incomplete disk */
bkid.bk_seqn = id;
strcpy(filespec,drive);
strcat(filespec,"SQZID.@@@");
#ifdef MSDMSC
fd = open(filespec,BWRITE,BPERMS);
#else
fd = creat(filespec,BWRITE);
#endif
write(fd,&bkid,sizeof(bkid));
close(fd);
}
/* read SQZID.@@@ */
get_bkupid(drive)
char *drive;
{
char filespec[16];
int fd;
strcpy(filespec,drive);
strcat(filespec,"SQZID.@@@");
if((fd = open(filespec,BREAD)) < 0)
return(0);
read(fd,&bkid,sizeof(bkid));
close(fd);
return((INT) bkid.bk_seqn);
}
hasbad(trg)
char *trg;
{
char c;
while (c = *trg++)
if (c == '*' || c == '?' || c == '.' || c == '\\' || c == '/')
return TRUE;
return FALSE;
}
/* write out standard 128 byte "RESTORE" header */
wrt_sqhead(infile)
char *infile;
{
char *strchr(), *col_ptr;
sqid.sq_fflag = FIN_CHR;
sqid.sq_span = span_cnt;
if((col_ptr = strchr(infile,':')) != NULL)
{
strcpy(sqid.sq_path, ++col_ptr);
}
else
strcpy(sqid.sq_path,infile);
put_sqhdr();
free_space -= ((long) sizeof(sqid));
}
/* write "RESTORE" header */
put_sqhdr()
{
long fseek();
fseek(outbuff,0L,0);
fwrite(&sqid,sizeof(sqid),1,outbuff);
}
/* read "RESTORE" header */
get_sqhdr()
{
long fseek();
fseek(outbuff,0L,0);
fread(&sqid,sizeof(sqid),1,outbuff);
}
/* get file directory
*/
#ifdef MSDMSC
unsigned char *malloc();
unsigned char *realloc();
struct ff_str {
char dummy[21]; /* reserved for dos */
unsigned char attribute; /* returned attribute */
unsigned time;
unsigned date;
long size; /* size of file */
unsigned char fn[13]; /* string containing the filename */
};
char *filedir(filename,mode)
unsigned char *filename;
unsigned mode;
{
union REGS srvi, srvo;
struct SREGS segregs;
unsigned int ds, dx;
struct ff_str ff_area;
unsigned char *result;
int reslen=0;
#ifdef M_I86LM /* large model ? */
ds = FP_SEG(filename);
dx = FP_OFF(filename);
#else
segread(&segregs); /* get ds value */
dx = (unsigned) filename;
ds = segregs.ds;
#endif
srvi.x.dx = (unsigned int) &ff_area; /* set DTA */
srvi.h.ah = 0x1a;
intdosx(&srvi, &srvo, &segregs);
srvi.x.cx = mode; /* set search modes */
srvi.h.ah = 0x4e; /* find first */
srvi.x.dx = dx;
segregs.ds = ds;
intdosx(&srvi, &srvo, &segregs);
if(srvo.x.cflag)
return(NULL);
if((result = malloc(strlen(ff_area.fn) + 1)) == NULL)
return(result);
reslen = strlen(ff_area.fn) + 1;
strcpy(result, ff_area.fn);
srvi.h.ah = 0x4f; /* find next */
for(;;)
{
segregs.ds = ds;
intdosx(&srvi, &srvo, &segregs);
if(srvo.x.cflag)
{
result = realloc(result, reslen + 1);
if(result != NULL)
*(result + reslen) = '\0';
return(result);
}
result=realloc(result,reslen+strlen(ff_area.fn)+1);
if(result==NULL)
return NULL; /* no memory left */
strcpy(result+reslen,ff_area.fn);
reslen += (strlen(ff_area.fn) + 1);
}
}
#endif
#ifdef CPMC86
unsigned char *malloc();
unsigned char *realloc();
char *filedir(filename)
unsigned char *filename;
{
char fcb[36];
char tmpfile[30];
char dma[128];
struct { unsigned int ax,bx,cx,dx,si,di,ds,es;} srvi, srvo;
struct { unsigned int scs, sss, sds, ses;} segs;
unsigned int dataseg, fcboff;
unsigned char *result;
int reslen;
int i;
reslen = 0;
segread(&segs); /* get ds value */
fcboff = (unsigned) &fcb[0]; /* get fcb offset */
dataseg = segs.sds;
srvi.dx = (unsigned int) &dma[0]; /* set DMA */
srvi.cx = 0x001a; /* set offset */
sysint(224, &srvi, &srvo);
srvi.dx = dataseg;
srvi.cx = 0x0033; /* set segment */
sysint(224, &srvi, &srvo);
srvi.cx = 0x0011; /* find first */
srvi.dx = fcboff;
srvi.ds = dataseg;
if(!setfcb(fcb, filename))
return(NULL);
sysint(224, &srvi, &srvo);
if((srvo.ax & 0x00ff) == 255)
return(NULL);
hackname(tmpfile, dma + (srvo.ax * 32));
if((result = malloc(strlen(tmpfile) + 1)) == NULL)
return(result);
reslen = strlen(tmpfile) + 1;
strcpy(result, tmpfile);
srvi.cx = 0x0012; /* find next */
for(;;)
{
sysint(224, &srvi, &srvo);
if(srvo.ax == 255)
{
result = realloc(result, reslen + 1);
if(result != NULL)
*(result + reslen) = '\0';
return(result);
}
hackname(tmpfile, dma + (srvo.ax * 32));
result=realloc(result,reslen+strlen(tmpfile)+1);
if(result==NULL)
return NULL; /* no memory left */
strcpy(result+reslen,tmpfile);
reslen += (strlen(tmpfile) + 1);
}
}
#endif
#ifdef CPMC86
dsk_reset(drv) /* oh how I love CP/M!! */
char *drv;
{
struct { unsigned int ax,bx,cx,dx,si,di,ds,es;} srvi, srvo;
unsigned int vector;
int drvno;
drvno = toupper(*drv) - 65;
vector = 0x0001;
if(drvno != 0)
vector <<= drvno;
srvi.dx = vector;
srvi.cx = 0x0025; /* disk reset fn */
sysint(224, &srvi, &srvo);
return(srvo.ax);
}
#else
dsk_reset(drv)
char *drv;
{
return(0);
}
#endif
#ifdef MSDMSC
abort(string)
char *string;
{
fprintf(stderr,"\n\n%s\n\n", string);
exit(1);
}
#endif